package com.bridgeintelligent.tag.engine.mvc.service.impl;

import com.bridgeintelligent.tag.engine.api.looklike.GroupItem;
import com.bridgeintelligent.tag.engine.api.looklike.LookLikeQueryModel;
import com.bridgeintelligent.tag.engine.api.looklike.LookLikeResult;
import com.bridgeintelligent.tag.engine.es.base.ESQueryUtils;
import com.bridgeintelligent.tag.engine.mvc.service.ILookLikeAggreagtionService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.MultiSearchRequest;
import org.elasticsearch.action.search.MultiSearchResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.range.Range;
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.Max;
import org.elasticsearch.search.aggregations.metrics.MaxAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.Min;
import org.elasticsearch.search.aggregations.metrics.MinAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;


/**
 * xiangjialiang
 */
@Service
@Slf4j
@AllArgsConstructor
public class LookLikeAggreagtionServiceImpl implements ILookLikeAggreagtionService {

    private static final String SCORE = "score";

    private static final String SPLIT = "-";

    /**
     * 校验数字类型
     */
    final private Pattern pattern = Pattern.compile("^(\\-|\\+)?\\d+(\\.\\d+)?$");


    @Override
    public LookLikeResult lookLikeAnalysis(LookLikeQueryModel queryModel) throws IOException {

        //首先通过索引查询min-max值
        GroupItem limit = getMinMax(queryModel.getEsIndex());
        if (Double.parseDouble(limit.getScoreMax()) <= 0d ){
            return LookLikeResult.of().setCode(-1).setMessage("最大值小于等于0").setList(new ArrayList<>());
        }

        //如果是等距则先计算分组
        if(StringUtils.equals(queryModel.getPolicyType(),"02")){
            queryModel.setReturnNum(limit.getScoreMax());
            List<GroupItem> items = getItems(limit.getScoreMin(), limit.getScoreMax(), queryModel.getNum());
            queryModel.setItems(items);
        }
        //此处只走等距自定义，等频先不做
        if (StringUtils.equals(queryModel.getPolicyType(),"02") || StringUtils.equals(queryModel.getPolicyType(),"03")){
            return getHandler(queryModel);
        }

        return null;
    }

    @Override
    public LookLikeResult findScoreByIndex(String esIndex) throws IOException {
        GroupItem limit = getMinMax(esIndex);
        String minMax = limit.getScoreMin()+"-"+limit.getScoreMax();
        long count = getCount(esIndex);
        return LookLikeResult.of().setCode(0).setMinMax(minMax).setCount(count);
    }


    private GroupItem getMinMax(String esIndex) throws IOException {

        MultiSearchRequest request = new MultiSearchRequest();

        QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
        SearchSourceBuilder searchSourceBuilder1 = new SearchSourceBuilder();
        MinAggregationBuilder minAgg = AggregationBuilders.min("min_score").field(SCORE);
        searchSourceBuilder1.query(queryBuilder).aggregation(minAgg).trackTotalHits(true).size(0);
        SearchRequest searchRequest1 = new SearchRequest(esIndex).source(searchSourceBuilder1);

        SearchSourceBuilder searchSourceBuilder2 = new SearchSourceBuilder();
        MaxAggregationBuilder maxAgg = AggregationBuilders.max("max_score").field(SCORE);
        searchSourceBuilder2.query(queryBuilder).aggregation(maxAgg).trackTotalHits(true).size(0);
        SearchRequest searchRequest2 = new SearchRequest(esIndex).source(searchSourceBuilder2);
        request.add(searchRequest1).add(searchRequest2);

        MultiSearchResponse multiSearchResponse = ESQueryUtils.getClient().msearch(request, RequestOptions.DEFAULT);
        Min min = multiSearchResponse.getResponses()[0].getResponse().getAggregations().get("min_score");
        Max max = multiSearchResponse.getResponses()[1].getResponse().getAggregations().get("max_score");
        log.info("=========原始最小值：{}，原始最大值：{}",min.getValueAsString(),max.getValueAsString());
        return GroupItem.of().setScoreMin(realValue(min.getValueAsString())).setScoreMax(realValue(max.getValueAsString()));

    }

    private long getCount(String esIndex) throws IOException {
        SearchRequest searchRequest = new SearchRequest(esIndex);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
        sourceBuilder.query(queryBuilder).trackTotalHits(true).size(0);
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = ESQueryUtils.getClient().search(searchRequest, RequestOptions.DEFAULT);
        if (searchResponse != null){
            SearchHits hits = searchResponse.getHits();
            if (hits != null){
                TotalHits totalHits = hits.getTotalHits();
                if (totalHits != null){
                    return totalHits.value;
                }
            }
        }
        return 0L;


    }

    private LookLikeResult getHandler(LookLikeQueryModel queryModel) throws IOException {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
        RangeAggregationBuilder rangeBuilder = AggregationBuilders.range("range_score").field(SCORE);
        for(GroupItem item:queryModel.getItems()){
            String low = item.getScoreMin();
            String high = item.getScoreMax();
            if (high.equals("∞")){
                rangeBuilder.addUnboundedFrom(low+"-"+high,new Double(low));
            }else{
                rangeBuilder.addRange(low+"-"+high,new Double(low),new Double(high));
            }
        }
        searchSourceBuilder.query(queryBuilder).aggregation(rangeBuilder).trackTotalHits(true).size(0);
        SearchRequest searchRequest = new SearchRequest(queryModel.getEsIndex()).source(searchSourceBuilder);
        SearchResponse response = ESQueryUtils.getClient().search(searchRequest, RequestOptions.DEFAULT);
        Range terms = response.getAggregations().get("range_score");
        List<LookLikeResult.R> data = new ArrayList<>();
        for (Range.Bucket entry : terms.getBuckets()) {
            LookLikeResult.R.of();
            if (entry.getKeyAsString().contains("∞")){
                data.add(LookLikeResult.R.of().setTitle(entry.getKeyAsString().replace("∞",queryModel.getReturnNum())).setCount(entry.getDocCount()).setFlag("∞"));
            }else{
                data.add(LookLikeResult.R.of().setTitle(entry.getKeyAsString()).setCount(entry.getDocCount()));
            }
        }
        sort(data);
        int count = 1;
        for (LookLikeResult.R r:data) {
            r.setPeopleName(queryModel.getPeopleName()+"-"+count);
            count++;
        }
        return LookLikeResult.of().setCode(0).setList(data).setMessage("查询成功");
    }

    private List<GroupItem> getItems(String min, String max, int n){
        List<GroupItem> list = new ArrayList();
        if (n <= 1){
            list.add(GroupItem.of().setScoreMin(min).setScoreMax("∞"));
            return list;
        }
        BigDecimal endStore = new BigDecimal(max);//l
        BigDecimal startSore = new BigDecimal(min);//m
        BigDecimal offset = (endStore.subtract(startSore)).divide(new BigDecimal(String.valueOf(n)),5,BigDecimal.ROUND_HALF_UP);
        log.info("==============即将对区间["+startSore+","+endStore+"],等分"+n+"份,间距："+offset);
        String minScore ;
        String maxScore;
        for (int i = 0; i < n; i++){
            if (i == n-1){
                minScore = String.valueOf(startSore.add(new BigDecimal(i).multiply(offset)).setScale(2,BigDecimal.ROUND_HALF_UP));
                maxScore = "∞";
            }else{
                minScore = String.valueOf(startSore.add(new BigDecimal(i).multiply(offset)).setScale(2,BigDecimal.ROUND_HALF_UP));
                maxScore = String.valueOf(startSore.add(new BigDecimal(i+1).multiply(offset)).setScale(2,BigDecimal.ROUND_HALF_UP));
            }
            list.add(GroupItem.of().setScoreMin(minScore).setScoreMax(maxScore));
        }
        return list;
    }

    private String realValue(String valueStr){
        valueStr = Optional.ofNullable(valueStr).orElse("0.00");
        if (pattern.matcher(valueStr).matches()){
            return String.valueOf(new BigDecimal(valueStr).setScale(2, BigDecimal.ROUND_HALF_UP));
        }else if(valueStr.contains("Infinity")){
            return "0.00";
        }else{
            return new BigDecimal(valueStr).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString();
        }
    }

    private void sort(List<LookLikeResult.R> data) {
        data.sort((o1, o2) -> {
            Double v1 = Double.parseDouble(o1.getTitle().split(SPLIT)[0]);
            Double v2 = Double.parseDouble(o2.getTitle().split(SPLIT)[0]);
            return v2.compareTo(v1);
        });
    }




}
